﻿using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using MplusM.MovieDatabaseWorkers;
using System.Globalization;
using System.Text;
using System.Security.Cryptography;

namespace cz.muni.fi.pb138
{
	/// <summary>
	/// Class representing the media stored in the database.
	/// </summary>
    public class Media
    {
        private String originalName;

        public long Id {get;set;}
        public String OriginalName { 
			get { return originalName;}
			set {
				originalName = value; 
				
				ASCIIEncoding encoding = new ASCIIEncoding();
				Byte[] bytes = encoding.GetBytes(originalName);
				
				SHA256Managed sha = new SHA256Managed ();
				Id = BitConverter.ToInt64(sha.ComputeHash(bytes),0); }
		}
        public String TranslatedName { get; set; }
        public String Country { get; set; }
        public DateTime ReleaseDate { get; set; }
        public IList<String> Genres { get; set; }
        public IList<String> Cast { get; set; }
        public String Director { get; set; }
        public double Rating { get; set; }
        public String Content { get; set; }
        public uint Length { get; set; }
        public Image Image { get; set; }


        /// <summary>
		/// an operator to perform an implicit conversions from XElement to Media type
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        static public implicit operator Media(XElement element)
        {
            if (element == null) throw new ArgumentNullException("element");
            
            return new Media
            {
                Id = (long)element.Element("id"),
                OriginalName = (String)element.Element("originalName"),
                TranslatedName = (String)element.Element("translatedName"),
                Country = (String)element.Element("country"),
                Director = (String)element.Element("director"),
                Content = (String)element.Element("content"),
                Length = (uint)element.Element("length"),
                ReleaseDate = (DateTime)element.Element("releaseDate"),
                Rating = (double)element.Element("rating"),
                Cast = GetCast(element.Element("cast")),
                Genres = GetGenres(element.Element("genres")),
                Image = GetImage(element.Element("image"))
            };
            
        }

		/// <summary>
		/// an operator to perform an implicit conversion from Media to XElement
		/// </summary>
		/// <param name="media"></param>
		/// <returns></returns>
        static public implicit operator XElement(Media media)
        {
            if (media == null) throw new ArgumentNullException("media");

            XElement root = new XElement("medium",
                new XElement("id", media.Id),
                new XElement("originalName", media.OriginalName),
                new XElement("translatedName", media.TranslatedName),
                new XElement("country", media.Country),
                new XElement("director", media.Director),
                new XElement("content", media.Content),
                new XElement("length", media.Length.ToString("G", CultureInfo.CreateSpecificCulture("en-US"))),
                new XElement("releaseDate", media.ReleaseDate),
                new XElement("rating", media.Rating.ToString("G", CultureInfo.CreateSpecificCulture("en-US"))),
                GetCastElement(media.Cast),
                GetGenresElement(media.Genres),
                GetImageElement(media.Image)
                );

            return root;
        }

		/// <summary>
		/// 
		/// </summary>
		/// <param name="cast"></param>
		/// <returns></returns>
        private static XElement GetCastElement(IList<string> cast)
        {
            XElement root = new XElement("cast");
            foreach (var actor in cast)
            {
                root.Add(new XElement("actor", actor));
            }

            return root;
        }

		/// <summary>
		/// gets genres of this media in an XML format
		/// </summary>
		/// <param name="genres"></param>
		/// <returns></returns>
        private static XElement GetGenresElement(IList<string> genres)
        {
            XElement root = new XElement("genres");

            foreach (var genre in genres)
            {
                root.Add(new XElement("genre", genre));
            }

            return root;
        }

		/// <summary>
		/// gets serialised Base64String image as an XML element
		/// </summary>
		/// <param name="img"></param>
		/// <returns></returns>
        private static XElement GetImageElement(Image img)
        {
            XElement imgElement = new XElement("image");
            if (img == null)
            {
                return imgElement;
            }
            MemoryStream stream = new MemoryStream();
            byte[] imageBytes = null;
            ImageFormat format = null;
            switch (img.MimeType())
            {
                case "image/gif":
                    format = ImageFormat.Gif;
                    break;
                case "image/png":
                    format = ImageFormat.Png;
                    break;
                case "image/tiff":
                    format = ImageFormat.Tiff;
                    break;
                default:
                    format = ImageFormat.Jpeg;
                    break;
            }
            try
            {
                Bitmap bmp = new Bitmap(img);
                bmp.Save(stream, format);
                imageBytes = stream.ToArray();
            }
            catch (Exception)
            {
                return imgElement;
            }
            finally
            {
                if (stream != null) stream.Close();
            }
            imgElement.SetValue(Convert.ToBase64String(imageBytes));
            return imgElement;
        }

		/// <summary>
		/// gets list of all cast from the given XElement structure
		/// </summary>
		/// <param name="element"></param>
		/// <returns></returns>
        private static IList<string> GetCast(XElement element)
        {
            if (element == null) throw new ArgumentNullException("element");

            return element.Elements("actor").Select(x => x.Value).ToList();
        }

		/// <summary>
		/// gets list of all genres from the given XElement structure
		/// </summary>
		/// <param name="element"></param>
		/// <returns></returns>
        private static IList<string> GetGenres(XElement element)
        {
            if (element == null) throw new ArgumentNullException("genres");

            return element.Elements("genre").Select(x => x.Value).ToList();
        }

		/// <summary>
		/// restores an Image from the given XElement
		/// </summary>
		/// <param name="element"></param>
		/// <returns></returns>
        private static Image GetImage(XElement element)
        {
            if (element == null || String.IsNullOrEmpty(element.Value))
            {
                return null;
            }
            MemoryStream stream = null;
            Image result = null;
            try
            {
                stream = new MemoryStream(Convert.FromBase64String(element.Value));
                result = Image.FromStream(stream);
            }
            catch (FormatException)
            {
                return null;
            }
            finally
            {
                if (stream != null) stream.Close();
            }
            return result;
        }
    }

}
